/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#include "driver/uart.h"
#include "string.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"

#include "stdinc.h"
#include "ble_err.h"
#include "ble_spp_server.h"
#include "checksum.h"

#define LOGTAG "SZW:BLE_SPP_SERVER"


#define ESP_SPP_APP_ID              0x56

/// SPP Service
static const uint16_t spp_service_uuid = 0xABF0;
/// Characteristic UUID
#define ESP_GATT_UUID_SPP_DATA_RECEIVE      0xABF1
#define ESP_GATT_UUID_SPP_DATA_NOTIFY       0xABF2

///Attributes State Machine
typedef enum {
    SPP_IDX_SVC,

    SPP_IDX_SPP_DATA_RECV_CHAR,
    SPP_IDX_SPP_DATA_RECV_VAL,

    SPP_IDX_SPP_DATA_NOTIFY_CHAR,
    SPP_IDX_SPP_DATA_NTY_VAL,
    SPP_IDX_SPP_DATA_NTF_CFG,

    SPP_IDX_MAX,
} spp_handle_idx_t;

#define ADV_FIXED_LEN 7
#define ADV_BLE_NAME_MAX_LEN 18

// 广播数据最大长度(31字节 - 1字节长度指示 = 30字节)
static char spp_adv_data[ADV_FIXED_LEN + (2 + ADV_BLE_NAME_MAX_LEN + 1)] = {
    0x02,0x01,0x06,       // 广播标记头
    0x03,0x03,0xF0,0xAB,  // 广播 SPP_SERVER 服务接口
    //0x0F,0x09, 0x45,0x53,0x50,0x5f,0x53,0x50,0x50,0x5f,0x53,0x45,0x52,0x56,0x45,0x52 // ="ESP_SPP_SERVER"
};

static const char *device_name = "SZW_SPP_SERVER";

// 注意：SPP MTU/最大一帧传输净荷数量为 20 字节！
static uint16_t spp_mtu_size = 23;
static esp_gatt_if_t spp_gatts_if = 0xff;

// 蓝牙是否已经连接
static bool is_connected = FALSE;
// 对端的蓝牙设备地址（MAC 地址）
static esp_bd_addr_t spp_peer_mac = {0x00};
// 对端的连接 ID(用于支持多连接？)
static uint16_t spp_peer_id = 0xffff;
// 对端是否启用了数据监听
static bool enable_data_ntf = FALSE;

// 应用层回调函数
static spp_server_callbacks_t *callbacks = NULL;

static uint16_t spp_handle_table[SPP_IDX_MAX];

static esp_ble_adv_params_t spp_adv_params = {
    .adv_int_min        = 0x20,
    .adv_int_max        = 0x40,
    .adv_type           = ADV_TYPE_IND,
    .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,
    .channel_map        = ADV_CHNL_ALL,
    .adv_filter_policy  = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst spp_profile = {
	.gatts_cb = gatts_profile_event_handler,
	.gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
};

/**
 * SPP PROFILE ATTRIBUTES
 */
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;

static const uint8_t character_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_NOTIFY;
static const uint8_t character_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE_NR|ESP_GATT_CHAR_PROP_BIT_READ;

/// SPP Service - data receive characteristic, read&write without response
static const uint16_t spp_data_receive_uuid = ESP_GATT_UUID_SPP_DATA_RECEIVE;
static const uint8_t  spp_data_receive_val[20] = {0x00};  // 用于接收数据的缓冲区 ?
#define SPP_DATA_MAX_LEN (512)

/// SPP Service - data notify characteristic, notify&read
static const uint16_t spp_data_notify_uuid = ESP_GATT_UUID_SPP_DATA_NOTIFY;
static const uint8_t  spp_data_notify_val[20] = {0x00};
static const uint8_t  spp_data_notify_ccc[2] = {0x00, 0x00};

/// Full HRS Database Description - Used to add attributes into the database
/// GATTS 数据表
static const esp_gatts_attr_db_t spp_gatt_db[SPP_IDX_MAX] = {
    // 声明主服务为 SPP 服务
    [SPP_IDX_SVC] =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t*)&primary_service_uuid, ESP_GATT_PERM_READ,
    sizeof(spp_service_uuid), sizeof(spp_service_uuid), (uint8_t*)&spp_service_uuid}},

	// 声明 SPP 数据接收特征
	// 1. 声明特征权限
    [SPP_IDX_SPP_DATA_RECV_CHAR] =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ,
    CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t*)&character_prop_read_write}},

    // 2. 声明特征值
    [SPP_IDX_SPP_DATA_RECV_VAL] =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t*)&spp_data_receive_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
    SPP_DATA_MAX_LEN, sizeof(spp_data_receive_val), (uint8_t*)spp_data_receive_val}},

    // 声明 SPP 数据通知特征
	// 1. 声明特征权限
    [SPP_IDX_SPP_DATA_NOTIFY_CHAR] =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ,
    CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t*)&character_prop_read_notify}},

    // 2. 声明特征值
    [SPP_IDX_SPP_DATA_NTY_VAL] =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t*)&spp_data_notify_uuid, ESP_GATT_PERM_READ,
    SPP_DATA_MAX_LEN, sizeof(spp_data_notify_val), (uint8_t*)spp_data_notify_val}},

    // 3. 声明特征配置值 CCC - Client Characteristic Configuration Descriptor
    [SPP_IDX_SPP_DATA_NTF_CFG] =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t*)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
    sizeof(uint16_t), sizeof(spp_data_notify_ccc), (uint8_t*)spp_data_notify_ccc}},
};

/// GATTS 表查询
static uint8_t find_char_and_desr_index(uint16_t handle) {
    for(int i = 0; i < SPP_IDX_MAX ; i++) {
        if(handle == spp_handle_table[i]) { return i; }
    }
    return 0xFF;
}

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
    esp_err_t err;
    log("GAP-EVENT: %s", esp_gap_ble_cb_event_name(event));

    switch (event) {
    case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
        esp_ble_gap_start_advertising(&spp_adv_params);
        break;
    case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
        // advertising start complete event to indicate advertising start successfully or failed
        if((err = param->adv_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
            loge("Advertising start failed: %s", esp_err_to_name(err));
        }
        break;
    default:
        break;
    }
}

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    uint8_t res = 0xff;

    switch (event) {
    	case ESP_GATTS_REG_EVT: {
    	    logi("%s %d", __func__, __LINE__);

    	    // 设置设备名称（不是蓝牙名称，蓝牙名称是在广播数据 spp_adv_data 中设置的）
    	    esp_ble_gap_set_device_name(device_name);
    	    // 开始广播（包含蓝牙名称）
        	esp_ble_gap_config_adv_data_raw((uint8_t*)spp_adv_data, strlen(spp_adv_data));
        	// 创建 ATT 属性表
        	esp_ble_gatts_create_attr_tab(spp_gatt_db, gatts_if, SPP_IDX_MAX, 0);
        	break;
    	}
    	case ESP_GATTS_READ_EVT: {
            res = find_char_and_desr_index(param->read.handle);
            logi("spp_handle_idx = %d", res);

            // TODO 客户端读取 SPP 状态特征 (client read the status characteristic)
            //if(res == SPP_IDX_SPP_STATUS_VAL) {
            //}
            break;
    	}
    	case ESP_GATTS_WRITE_EVT: {
    	    res = find_char_and_desr_index(param->write.handle);
            logi("spp_handle_idx = %d", res);
            if(param->write.is_prep == FALSE) {
                if(res == SPP_IDX_SPP_DATA_NTF_CFG) {
                	// DATA 通知使能配置
                    if((param->write.len == 2)&&(param->write.value[0] == 0x01)&&(param->write.value[1] == 0x00)) {
                        enable_data_ntf = TRUE;
                    } else if((param->write.len == 2)&&(param->write.value[0] == 0x00)&&(param->write.value[1] == 0x00)) {
                        enable_data_ntf = FALSE;
                    }
                }
                else if(res == SPP_IDX_SPP_DATA_RECV_VAL) {
                    // 接收数据事件处理
                	if(callbacks->onRxFrame) { callbacks->onRxFrame((uint8_t*)param->write.value, param->write.len); }
                }
                else {
                    // TODO:处理其它 SPP 事件
                }
            } else if((param->write.is_prep == TRUE)&&(res == SPP_IDX_SPP_DATA_RECV_VAL)) {
                // 接收数据事件处理
            	if(callbacks->onRxFrame) { callbacks->onRxFrame((uint8_t*)param->write.value, param->write.len); }
            }
      	 	break;
    	}
    	case ESP_GATTS_EXEC_WRITE_EVT: {
    	    if(param->exec_write.exec_write_flag) {
                logi("Receive big data end (ESP_GATTS_EXEC_WRITE_EVT). ");
    	        //print_write_buffer();
    	        //free_write_buffer();
    	    }
    	    break;
    	}
    	case ESP_GATTS_MTU_EVT:
    	    spp_mtu_size = param->mtu.mtu;
    	    break;
    	case ESP_GATTS_CONF_EVT:
    	    break;
    	case ESP_GATTS_UNREG_EVT:
        	break;
    	case ESP_GATTS_DELETE_EVT:
        	break;
    	case ESP_GATTS_START_EVT:
        	break;
    	case ESP_GATTS_STOP_EVT:
        	break;
    	case ESP_GATTS_CONNECT_EVT: {
    	    spp_peer_id = param->connect.conn_id;
    	    spp_gatts_if = gatts_if;
    	    is_connected = TRUE;
    	    memcpy(&spp_peer_mac, &param->connect.remote_bda, sizeof(esp_bd_addr_t));

    	    // BLE 连接建立事件处理
            if(callbacks && callbacks->onConnect) { callbacks->onConnect(spp_peer_mac); }
        	break;
    	}
    	case ESP_GATTS_DISCONNECT_EVT: {
    	    is_connected = FALSE;
    	    enable_data_ntf = FALSE;
    	    esp_ble_gap_start_advertising(&spp_adv_params);

    	    // BLE 连接断开事件处理
            if(callbacks && callbacks->onDisconnect) { callbacks->onDisconnect(spp_peer_mac); }
    	    break;
    	}
    	case ESP_GATTS_OPEN_EVT:
    	    break;
    	case ESP_GATTS_CANCEL_OPEN_EVT:
    	    break;
    	case ESP_GATTS_CLOSE_EVT:
    	    break;
    	case ESP_GATTS_LISTEN_EVT:
    	    break;
    	case ESP_GATTS_CONGEST_EVT:
    	    break;
    	case ESP_GATTS_CREAT_ATTR_TAB_EVT: {
    	    logi("Handle count: %d", param->add_attr_tab.num_handle);
    	    if (param->add_attr_tab.status != ESP_GATT_OK) {
    	        loge("Create attribute table failed, error code=0x%X", param->add_attr_tab.status);
    	    }
    	    else if (param->add_attr_tab.num_handle != SPP_IDX_MAX) {
    	        loge("Create attribute table abnormally, handle count (%d) doesn't equal to SPP_IDX_MAX(%d)", param->add_attr_tab.num_handle, SPP_IDX_MAX);
    	    }
    	    else {
    	        memcpy(spp_handle_table, param->add_attr_tab.handles, sizeof(spp_handle_table));
    	        esp_ble_gatts_start_service(spp_handle_table[SPP_IDX_SVC]);
    	    }
    	    break;
    	}
    	default:
    	    break;
    }
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    logd("GATTS[%d]-EVENT: %s", gatts_if, esp_gatts_cb_event_name(event));

    /* If event is register event, store the gatts_if for each profile */
    if (event == ESP_GATTS_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
        	spp_profile.gatts_if = gatts_if;
        } else {
            loge("Reg app failed, app_id %04x, status %d", param->reg.app_id, param->reg.status);
            return;
        }
    }

	/* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
    if (gatts_if == ESP_GATT_IF_NONE || gatts_if == spp_profile.gatts_if) {
        if (spp_profile.gatts_cb) {
        	spp_profile.gatts_cb(event, gatts_if, param);
        }
    }
}

esp_err_t ble_spp_server_start(const char *bleName, spp_server_callbacks_t *_callbacks) {
	// 在广播数据中设置蓝牙名称
	spp_adv_data[ADV_FIXED_LEN] = 1 + strlen(bleName);
	spp_adv_data[ADV_FIXED_LEN + 1] = 0x09; // Complete Local Name
	strncpy(spp_adv_data + ADV_FIXED_LEN + 2, bleName, ADV_BLE_NAME_MAX_LEN); // 蓝牙名称

	callbacks = _callbacks;

    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);
    esp_ble_gatts_app_register(ESP_SPP_APP_ID);

    return ESP_OK;
}

esp_err_t ble_spp_server_stop() {
	return esp_ble_gatts_app_unregister(ESP_SPP_APP_ID);
}

static esp_err_t ble_spp_server_send(uint16_t peer, spp_handle_idx_t port, const void *data, int len) {
	if(!data || !len) { return ESP_ERR_INVALID_ARG; }
	if(!is_connected) { return ESP_ERR_INVALID_STATE; }
	if(port == SPP_IDX_SPP_DATA_NTY_VAL && !enable_data_ntf) { loge("do not enable data Notify"); return ESP_ERR_NOT_SUPPORTED; }

	int mtu = (spp_mtu_size - 3);
	int sliceCount = len/mtu + ((len%mtu) ? 1 : 0);
	int sliceLen;

	for(int i=0; i < sliceCount; i++) {
		sliceLen = MIN(mtu, len);
		esp_ble_gatts_send_indicate(spp_gatts_if, peer, spp_handle_table[port], sliceLen, (uint8_t*)(data + i*mtu), FALSE); len -= sliceLen;
		osDelayMs(20);
	}
	return ESP_OK;
}

esp_err_t ble_spp_server_data_notify(const void *data, int len) {
	return ble_spp_server_send(spp_peer_id, SPP_IDX_SPP_DATA_NTY_VAL, data, len);
}

uint8_t* ble_spp_server_get_peer_mac() {
	return is_connected ? spp_peer_mac : NULL;
}

/*
typedef struct spp_receive_data_node{
    int32_t len;
    uint8_t * node_buff;
    struct spp_receive_data_node * next_node;
}spp_receive_data_node_t;

static spp_receive_data_node_t * temp_spp_recv_data_node_p1 = NULL;
static spp_receive_data_node_t * temp_spp_recv_data_node_p2 = NULL;

typedef struct spp_receive_data_buff{
    int32_t node_num;
    int32_t buff_size;
    spp_receive_data_node_t * first_node;
}spp_receive_data_buff_t;

static spp_receive_data_buff_t SppRecvDataBuff = {
    .node_num   = 0,
    .buff_size  = 0,
    .first_node = NULL
};

static bool store_wr_buffer(esp_ble_gatts_cb_param_t *p_data) {
    temp_spp_recv_data_node_p1 = (spp_receive_data_node_t *)malloc(sizeof(spp_receive_data_node_t));

    if(temp_spp_recv_data_node_p1 == NULL) {
        logi("malloc error %s %d", __func__, __LINE__);
        return FALSE;
    }
    if(temp_spp_recv_data_node_p2 != NULL) {
        temp_spp_recv_data_node_p2->next_node = temp_spp_recv_data_node_p1;
    }
    temp_spp_recv_data_node_p1->len = p_data->write.len;
    SppRecvDataBuff.buff_size += p_data->write.len;
    temp_spp_recv_data_node_p1->next_node = NULL;
    temp_spp_recv_data_node_p1->node_buff = (uint8_t*)malloc(p_data->write.len);
    temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1;
    memcpy(temp_spp_recv_data_node_p1->node_buff,p_data->write.value,p_data->write.len);
    if(SppRecvDataBuff.node_num == 0) {
        SppRecvDataBuff.first_node = temp_spp_recv_data_node_p1;
        SppRecvDataBuff.node_num++;
    } else {
        SppRecvDataBuff.node_num++;
    }

    return TRUE;
}

static void free_write_buffer(void) {
    temp_spp_recv_data_node_p1 = SppRecvDataBuff.first_node;

    while(temp_spp_recv_data_node_p1 != NULL) {
        temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1->next_node;
        free(temp_spp_recv_data_node_p1->node_buff);
        free(temp_spp_recv_data_node_p1);
        temp_spp_recv_data_node_p1 = temp_spp_recv_data_node_p2;
    }

    SppRecvDataBuff.node_num = 0;
    SppRecvDataBuff.buff_size = 0;
    SppRecvDataBuff.first_node = NULL;
}

static void print_write_buffer(void) {
    temp_spp_recv_data_node_p1 = SppRecvDataBuff.first_node;

    while(temp_spp_recv_data_node_p1 != NULL) {
        //uart_write_bytes(UART_NUM_0, (char *)(temp_spp_recv_data_node_p1->node_buff), temp_spp_recv_data_node_p1->len);
    	onRxPacket(NULL, (uint8_t*)(temp_spp_recv_data_node_p1->node_buff), temp_spp_recv_data_node_p1->len);
        temp_spp_recv_data_node_p1 = temp_spp_recv_data_node_p1->next_node;
    }
}
*/

/*
// 当 BLE SPP Server 接收到数据时回调
static FUNC_onBleSppServerRxByte onRxByteHandler = NULL;
static void *onRxByteHandlerCookie = NULL;

static void onRxPacket(const uint8_t *peer, const uint8_t *data, int len) {
	if(!onRxByteHandler || !data || !len) { return; }
	for(int i=0; i<len; i++) {
		onRxByteHandler(NULL, data[i], onRxByteHandlerCookie);
	}
}
*/
